'''
8.3 类的成员
    类的成员包括属性和方法,默认他们可以在类的外部被访问或者调用,但考虑到数据安全问题,有事需要将其设置为私有
成员,限制类外部对其进行访问或者调用.
'''

'''
8.3.1 属性
    属性按声明的方式可以分为两类:类属性和实例属性.
'''
'''
1.类属性
    类属性是声明在类内部,方法外部的属性.类属性可以通过类或者对象进行访问,但只能通过类进行修改
'''
# 例如,定义一个质保函类属性的Car类,创建Car类的对象,并分别通过类和对象访问,修改类属性,代码如下:
class Car:
    wheels = 4
car = Car()
print(Car.wheels)  # 通过类Car访问类属性   4
print(car.wheels)  # 通过对象car访问类属性  4
Car.wheels = 3     # 通过类Car修改类属性wheels
print(Car.wheels)  # 3
print(car.wheels)  # 3
car.wheels = 4     # 通过对象car修改类属性wheels
print(Car.wheels)  # 3
print(car.wheels)  # 4
print('---------------------')
'''
    分析输出结果中的前2个数据可知,Car类和car对象成功访问了类属性,结果都为4;分析中间的2个数据可知,Car
类成功修改了类属性的值,因此Car类和car对象访问的结果变为3;分析最后2个数据可知,Car类访问的类属性值仍然
是3,而car对象访问的结果变为4,说明car对象不能修改类属性的值.
    为什么通过car对象最后一次访问类属性的值为4?
    程序之所以会出现这个问题,是因为此处"car.wheels = 4"语句的作用等于添加了一个与类属性同名的实例属性
'''

'''
2. 实例属性
    实例属性是在方法内部声明的属性,Python支持动态添加实例属性
'''
# (1) 访问实例属性
    # 实例属性只能通过对象进行访问,例如定义一个包含方法和实例属性的类Car_1,创建Car_1类的对象,并访问实例属性,代码如下:
class Car_1:
    def drive(self):
        self.wheels = 4  # 添加实例属性
car_1 = Car_1()          # 创建对象car_1
car_1.drive()
print(car_1.wheels)
# print(Car_1.wheels)
print('---------------------')
'''
    以上代码首先定义了Car_1类,该类中包含了一个drive()方法,drive()防范中使用self关键字添加了一个实例属性
wheels;然后创建了一个Car_1类的对象car_1,对象car_1调用drive()方法为Car_1类添加实例属性;最后分别通过对象car_1
和类Car访问实例属性.
    运行代码,结果如下:
4
Traceback (most recent call last):
  File "D:/code/python/blackhorse_test/chapter_8 面向对象/8.3 类的成员.py", line 47, in <module>
    print(Car_1.wheels)
AttributeError: type object 'Car_1' has no attribute 'wheels'
    分析以上运行结果:程序通过对象car_1成功访问了实例属性,通过car访问实例属性时出现了错误,说明实例属性
只能通过对象访问,不能通过类访问
'''
# (2) 修改实例属性
# 实例属性通过对象进行修改,例如在以上示例中掺入修改实例的代码,具体如下:
car_1.wheels = 3
print(car_1.wheels)
print('---------------------')

# (3) 动态添加实例属性
# Python支持在类的外部使用对象动态地添加实例属性.例如,在以上示例的末尾动态添加实例属性color,
# 增加的代码如下:
car_1.color = '红色'   # 动态地添加实例属性
print(car_1.color)
print('---------------------')
# 从以上结果可以出,程序成功添加了实例属性,并通过对象访问了新增加的实例属性

'''
8.3.2 方法
    Python中的方法按定义方式和用途可以分为3类:实例方法.类方法和静态方法
'''
'''
1.实例方法
    实例方法形似函数,但它定义在类内部,以self为第1个形参.例如,8.2.2节中声明的drive()就是一个实例方法.
实例方法中的self参数代表对象本身,它会在实例方法被调用时,自动接收由系统传递的调用该方法的对象.
    实例方法只能通过对象调用.
'''
# 例如,定义一个包含实例方法drive()的类Car_2,创建Car_2类的对象,分别通过对象和类调用实例方法
# 代码如下:
class Car_2:
    def drive(self):
        print('我是实例方法')
car_2 = Car_2()
car_2.drive()
# Car_2.drive()
'''Traceback (most recent call last):
  File "D:/code/python/blackhorse_test/chapter_8 面向对象/8.3 类的成员.py", line 94, in <module>
    Car_2.drive()
TypeError: drive() missing 1 required positional argument: 'self'''
print('---------------------')
# 以上结果可以看出,程序通过对象成功调用了实例方法,通过类则无法调用实例方法

'''
2. 类方法
    类方法是定义在类内部,使用装饰器 @classmethod 修饰的方法.
'''
# 类方法的语法格式如下所示:
# @classmethod
# def 类方法名(cls):
#     方法体
# 类方法中第一个参数cls代表类本身,它会在类方法被调用时自动接收由系统传递的调用该方法的类.
# 例如,定义一个包含类方法stop()的Car_3类,代码如下:
class Car_3:
    @ classmethod
    def stop(cls):
        print('我是类方法')
        # 类方法可以通过类和对象调用,示例代码如下:
car_3 = Car_3()
Car_3.stop()
car_3.stop()
print('---------------------')
# 从以上运行结果看出,程序通过对象和类成功调用了类方法.
'''
类方法中可以使用cls访问和修改类属性的值.例如:定义一个包含类属性,类方法的Car_4类,并在类方法中使用cls访问和修改类属性,
然后创建Car_4类的对象car_4,对象car_4调用类方法.代码如下:
'''
class Car_4:
    wheels = 3
    @classmethod
    def stop(cls):
        print(cls.wheels)
        cls.wheels = 4
        print(cls.wheels)
car_4 = Car_4()
car_4.stop()
print('---------------------')
# 从运行结果看,程序在类方法stop()中成功访问和修改了类属性wheels的值

'''
3. 静态方法
    静态方法是定义在类内部,使用装饰器@staticmethod修饰的方法.静态方法的语法格式如下:
@staticmethod
def 静态方法名():
    方法体
# 与实例方法和类方法比较,静态方法没有任何默认参数,它适用于与类无关的操作,或者无需使用类成员的操作,常见于一些工具类中
'''
# 例如,定义一个包含静态方法的Car_5类,代码如下:
class Car_5:
    @staticmethod
    def test():
        print('我是静态方法')
# 静态方法可以通过类和对象调用,
# 例如创建Car_5类的对象car_5,分别通过对象和类调用静态方法,代码如下:
car_5 = Car_5()
car_5.test()
Car_5.test()
print('---------------------')
# 静态方法内部不能直接访问属性或方法,但可以使用类名访问类属性或调用类方法,示例代码如下:
class Car_6:
    wheels = 3
    @staticmethod
    def test():
        print('我是静态方法')
        print(f'类属性值为{Car_6.wheels}')
car_6 = Car_6
car_6.test()
print('---------------------')

'''
8.3.3 私有成员
    类的成员默认是公有成员,可以在类的外部通过类或者对象随意访问,这样显然不够安全.为了保证类中数据的安全,Python支持将公有成员改为私有成员,
在一定程度上限制在类的外部对类成员的访问.
    Python通过在类成员的名称前面添加 __ 双下划线的方式来表示私有成员,语法格式如下:
__属性名
__方法名
'''
# 例如,定义一个包含私有属性__wheels和私有方法__drive()的Car_7类,代码如下:
class Car_7:
    __wheels = 4          # 私有属性
    def __drive(self):
        print('行驶')      # 私有方法
# 以上示例中定义的2个私有成员在类的内部可以直接访问,在类的外部不能直接访问,但可以通过调用类的公有方法的方式进行访问:
    def test(self):
        print(f'轿车有{self.__wheels}个车轮')
        self.__drive()
car_7 = Car_7()
# print(car_7.__wheels)  # AttributeError: 'Car_7' object has no attribute '__wheels'
# car_7.__drive()  # AttributeError: 'Car_7' object has no attribute '__drive'
# 以上两行代码报错,说明在类的外部无法直接访问类的私有成员
car_7.test()
'''
轿车有4个车轮
行驶'''
# 从以上输出结果可以看出,在类的外部通过公有方法test()成功访问了私有属性__wheels,并调用了私有方法__drive().
# 由此可知,类的私有成员只能在类的内部直接访问,但可以在类的外部通过类的公有方法间接访问.



